package cs.ubbcluj.ro.algorithm;

import cs.ubbcluj.ro.datatypes.interfaces.Matrix;
import cs.ubbcluj.ro.datatypes.interfaces.Vector;
import cs.ubbcluj.ro.lp.LP;

public class AlgorithmWeightedCombined extends AlgorithmWeighted {
	
	Vector v;
	Vector x1;
	Vector y1;
	Vector s1;
	Vector x2;
	Vector y2;
	Vector s2;	
	
	boolean isUsingMu;

	public AlgorithmWeightedCombined(LP lp, double sigma, double rho, double epsilon, double theta) {
		super(lp, sigma, rho, epsilon, theta);
		
		x1 = lp.getX().createHelperVector(lp.getX().getNrOfRows());
		x1.set(lp.getX());
		
		x2 = lp.getX().createHelperVector(lp.getX().getNrOfRows());
		x2.set(lp.getX());
		
		y1 = lp.getY().createHelperVector(lp.getY().getNrOfRows());
		y1.set(lp.getY());
		
		y2 = lp.getY().createHelperVector(lp.getY().getNrOfRows());
		y2.set(lp.getY());
		
		s1 = lp.getS().createHelperVector(lp.getS().getNrOfRows());
		s1.set(lp.getS());
		
		s2 = lp.getS().createHelperVector(lp.getS().getNrOfRows());
		s2.set(lp.getS());
	}
	
    /**
     * calculates the duality gap given by the formula: |c^T * x - b^T * y| / (1 + |b^T * y|)
     * @return
     * @throws InterruptedException
     */
    public double dualityGap(Vector x, Vector y) throws InterruptedException {
    	Vector c = lp.getC().createHelperVector(lp.getC().getNrOfRows());
    	c.set(lp.getC());
    	
    	Vector b = lp.getB().createHelperVector(lp.getB().getNrOfRows());
    	b.set(lp.getB());
    	
    	double cx = c.product(x);
    	double by = b.product(y);
    	double dualityGap = Math.abs(cx - by) / (1 + Math.abs(cx));    	
    	return dualityGap;
    }
    
    /**
     * sets values for x given by the formula x = x + rho * alphaPrimal * deltaX
     * @throws InterruptedException
     */
    public void setX(Vector x) throws InterruptedException {    	
    	x.set(x.sum(deltaX.product(rho * alphaPrimal)));
    }
    
    /**
     * sets values for y given by the formula y = y + rho * alphaDual * deltaY
     * @throws InterruptedException
     */
    public void setY(Vector y) throws InterruptedException {
    	y.set(y.sum(deltaY.product(rho * alphaDual)));
    }
    
    /**
     * sets values for s given by the formula s = s + rho * alphaDual * deltaS
     * @throws InterruptedException
     */
    public void setS(Vector s) throws InterruptedException {
    	s.set(s.sum(deltaS.product(rho * alphaDual)));
    }
    
    /**
     * sets the value of w given by the formula w = (nu^2 * n) / norm(w^2)
     * @throws InterruptedException 
     */
    public void setWusingNu() throws InterruptedException {    	
    	Vector w = nu.createHelperVector(nu.getNrOfRows());
    	w.set(nu);
    	w.product(Math.sqrt(nu.getNrOfRows()) / lp.getW().norm());
    	lp.setW(w);    
    }
    
    void calculateVariablesUsingMu() throws InterruptedException {
    	// get value of mu
		mu = getMu();
					
		// get value of nu
		nu = calculateRightHandSide(true);
			
		// calculate the value of rb		
		rb = lp.getRb();
		
		// calculate the value of rc
		rc = lp.getRc();

		// calculate AXS^-1
		Matrix AXS_1 = lp.getAXS_1();
		
		// calculate AS^-1
		Matrix AS_1 = lp.getAS_1();
		
		// calculate deltaY
		Matrix AD2At = lp.getAD2At();			
		deltaY = getDeltaY(AD2At, AXS_1, AS_1);
		
//		System.out.println("\ndeltaY = ");
//		deltaY.print();
		
		// calculate deltaS
		deltaS = getDeltaS();
		
		// calculates deltaX
		deltaX = getDeltaX();
		
		// calculates alphaPrimal
		alphaPrimal = min(lp.getX(), deltaX);
		
		// calculates alphaDual
		alphaDual = min(lp.getS(), deltaS);

		// set values for x, y, s
		setX(x1);
		setY(y1);
		setS(s1);			
    }
    
    void calculateVariablesUsingW() throws InterruptedException {
    	// get value of mu
		//mu = getMu();
    	
    	//set value of w
    	if (isUsingMu) {
    		setWusingNu();
    		//setW();
    	} else {    	    	
    		setW();
    	}
					
		// get value of nu
		nu = calculateRightHandSide(false);
			
		// calculate the value of rb		
		rb = lp.getRb();
		
		// calculate the value of rc
		rc = lp.getRc();

		// calculate AXS^-1
		Matrix AXS_1 = lp.getAXS_1();
		
		// calculate AS^-1
		Matrix AS_1 = lp.getAS_1();
		
		// calculate deltaY
		Matrix AD2At = lp.getAD2At();			
		deltaY = getDeltaY(AD2At, AXS_1, AS_1);
		
//		System.out.println("\ndeltaY = ");
//		deltaY.print();
		
		// calculate deltaS
		deltaS = getDeltaS();
		
		// calculates deltaX
		deltaX = getDeltaX();
		
		// calculates alphaPrimal
		alphaPrimal = min(lp.getX(), deltaX);
		
		// calculates alphaDual
		alphaDual = min(lp.getS(), deltaS);

		// set values for x, y, s
		setX(x2);
		setY(y2);
		setS(s2);		
    }
    
    /**
	 * solves problem 
	 * @throws InterruptedException
	 */
	public void solveProblem() throws InterruptedException {	
		int iterationCount = 0;	
		int iterationMu = 0;
		int iterationW = 0;
		double dualityGapUsingMu = 0.0;
		double dualityGapUsingW = 0.0;
		
		do {	
			x1.set(lp.getX());
			y1.set(lp.getY());
			s1.set(lp.getS());
			x2.set(lp.getX());
			y2.set(lp.getY());			
			s2.set(lp.getS());
			
			calculateVariablesUsingMu();			
			dualityGapUsingMu = dualityGap(x1, y1);
			System.out.println("dualityGapUsingMu = " + dualityGapUsingMu);
			
			
			calculateVariablesUsingW();			
			dualityGapUsingW = dualityGap(x2, y2);
			System.out.println("dualityGapUsingW = " + dualityGapUsingW);			
			
			/*Vector aux = x1.createHelperVector(x1.getNrOfRows());
			aux.set(x1);						
			dualityGapUsingNu = centralityDegree(aux.productScalar(s1));
			aux.set(x2);						
			dualityGapUsingW = centralityDegree(aux.productScalar(s2));
			
			System.out.println("centralityDegreeNu = " + dualityGapUsingNu);
			System.out.println("centralityDegreeW = " + dualityGapUsingW);
			*/
			
			System.out.println("----------------------------");
						
			if (dualityGapUsingMu < dualityGapUsingW) {
				System.out.println("---- MU");
				isUsingMu = true;
				iterationMu++;
				lp.setX(x1);
				lp.setY(y1);
				lp.setS(s1);
				//setW();
			} else {
				System.out.println("---- W");
				iterationW++;
				lp.setX(x2);
				lp.setY(y2);
				lp.setS(s2);
				//setW();
			}
			
			iterationCount++;	
					
			System.out.println("Primal = " + lp.getPrimal());
			System.out.println("Dual= " + lp.getDual());
			System.out.println("iteration = " + iterationCount);
			
//			System.out.println("\nx = ");
//			lp.getX().print();
			
		} while (!isResultGoodEnough() && iterationCount < 50);		
		
		System.out.println("\niterationCount = " + iterationCount);
		System.out.println("iterationMu = " + iterationMu);
		System.out.println("iterationW = " + iterationW);
	}
    
    

}
